﻿using System;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Z1029.Chart.Convert
{
    class Program
    {
        public static bool UseFakeNotes = false;

        static void Main(string[] args)
        {
            log("欢迎使用，把pec文件拖到本软件上即可自动转换");
            log("Code by Z1029");
            if (args.Length == 0)
            {
                throw new NullReferenceException("未选择文件！请将pec文件拖到本软件上运行");
            }
            string path = args[0].Trim();
            if (!File.Exists(path))
                throw new FileNotFoundException("未选择文件！请将pec文件拖到本软件上运行");
            string pecFile = File.ReadAllText(path);
            string ppcjson = ToPPC(pecFile);//PhiPlayer的json文件
            File.WriteAllText(path + ".ppc", ppcjson);
            string phijson = ToPhi(ppcjson);
            File.WriteAllText(path + ".json", phijson);
            log("转换成功，谱面内容："+phijson);
            log("文件已保存到：" + path + ".json");
            log("感谢使用，按下任意键退出");
            Console.Read();
        }

        static string ToPhi(string ppc)
        {
            PhiChart phi = new PhiChart();
            Chart pc = JsonConvert.DeserializeObject<Chart>(ppc);
            phi.formatVersion = 3;
            phi.offset = pc.offset;
            phi.numOfNotes = pc.numOfNotes;
            phi.judgeLineList = new PhiJudgeLineSet[pc.judgeLineList.Length];
            for (int i = 0; i < pc.judgeLineList.Length; i++)
            {
                JudgeLineSet jl = pc.judgeLineList[i];
                PhiJudgeLineSet jls = new PhiJudgeLineSet
                {
                    bpm = jl.bpm
                };

                int j;
                for (j = 0; j < jl.alphaEvents.Length; j++)
                {
                    ValueSet s = new ValueSet();
                    s.start = Mathf.RoundToInt(jl.alphaEvents[j].start);
                    s.end = Mathf.RoundToInt(jl.alphaEvents[j].end);
                    s.startTime = Utils.GetBeatTimeFromTime(jl.alphaEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.alphaEvents[j].endTime, jl.bpm);
                    jls.judgeLineDisappearEvents.Add(s);
                }
                for (j = 0; j < jl.moveEvents.Length; j++)
                {
                    ValueSet s = new ValueSet();
                    s.start = (jl.moveEvents[j].start / 18) + 0.5f;
                    s.start2 = (jl.moveEvents[j].start2 / 10) + 0.5f;
                    s.end = (jl.moveEvents[j].end / 18) + 0.5f;
                    s.end2 = (jl.moveEvents[j].end2 / 10) + 0.5f;
                    s.startTime = Utils.GetBeatTimeFromTime(jl.moveEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.moveEvents[j].endTime, jl.bpm);
                    jls.judgeLineMoveEvents.Add(s);
                }
                for (j = 0; j < jl.rotateEvents.Length; j++)
                {
                    ValueSet s = new ValueSet();
                    s.start = Mathf.RoundToInt(jl.rotateEvents[j].start);
                    s.end = Mathf.RoundToInt(jl.rotateEvents[j].end);
                    s.startTime = Utils.GetBeatTimeFromTime(jl.rotateEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.rotateEvents[j].endTime, jl.bpm);
                    jls.judgeLineRotateEvents.Add(s);
                }
                for (j = 0; j < jl.speedEvents.Length; j++)
                {
                    SpeedEventV3 s = new SpeedEventV3();
                    s.startTime = Utils.GetBeatTimeFromTime(jl.speedEvents[j].startTime, jl.bpm);
                    s.endTime = Utils.GetBeatTimeFromTime(jl.speedEvents[j].endTime, jl.bpm);
                    try
                    {
                        SpeedEventV3 last = jls.speedEvents[j - 1];
                        s.floorPosition = last.floorPosition + (float)Utils.GetTimeFromBeatTime(last.endTime-last.startTime, jl.bpm) * last.value;
                    }
                    catch
                    {
                        s.floorPosition = 0;
                    }
                    //TODO：此处floorPosition计算经常错误

                    s.value = jl.speedEvents[j].value;
                    jls.speedEvents.Add(s);
                }
                for (j = 0; j < jl.notesAbove.Length; j++)
                {

                    NoteSet s = new NoteSet();
                    Note.NoteData d = jl.notesAbove[j];
                    s.time = d.beatTime;
                    s.speed = d.speed / 12;
                    s.holdTime = d.beatHoldTime;
                    s.positionX = d.positionX;
                    if (d.type == NoteType.Hold)
                        s.speed *= GetJudgelineSpeedV3((d.beatTime + d.beatHoldTime / 2), jls.speedEvents.ToArray());
                    s.floorPosition = GetSpeedDistance((float)Utils.GetTimeFromBeatTime(d.beatTime, jl.bpm), jl.speedEvents);
                    switch (d.type)
                    {
                        case NoteType.Click:
                            s.type = 1;
                            break;
                        case NoteType.Drag:
                            s.type = 2;
                            break;
                        case NoteType.Hold:
                            s.type = 3;
                            break;
                        case NoteType.Flick:
                            s.type = 4;
                            break;
                    }
                        jls.notesAbove.Add(s);
                }
                for (j = 0; j < jl.notesBelow.Length; j++)
                {

                    NoteSet s = new NoteSet();
                    Note.NoteData d = jl.notesBelow[j];
                    s.time = d.beatTime;
                    s.speed = d.speed / 12;
                    s.holdTime = d.beatHoldTime;
                    s.positionX = d.positionX;
                    if (d.type == NoteType.Hold)
                        s.speed *= GetJudgelineSpeedV3((d.beatTime + d.beatHoldTime / 2), jls.speedEvents.ToArray());
                    s.floorPosition = GetSpeedDistance((float)Utils.GetTimeFromBeatTime(d.beatTime, jl.bpm), jl.speedEvents);
                    switch (d.type)
                    {
                        case NoteType.Click:
                            s.type = 1;
                            break;
                        case NoteType.Drag:
                            s.type = 2;
                            break;
                        case NoteType.Hold:
                            s.type = 3;
                            break;
                        case NoteType.Flick:
                            s.type = 4;
                            break;
                    }
                        jls.notesBelow.Add(s);
                }
                jls.numOfNotesAbove = jls.notesAbove.Count;
                jls.numOfNotesBelow = jls.notesBelow.Count;
                jls.numOfNotes = jls.numOfNotesAbove + jls.numOfNotesBelow;
                phi.numOfNotes += jls.numOfNotes;
                phi.judgeLineList[i] = jls;
            }
            return JsonConvert.SerializeObject(phi, Formatting.None);
        }

        public static float GetSpeedDistance(float time, SpeedEvent[] speeds)
        {
            float num = 0;
            int index = speeds.Length - 1;
            for (int i = 0; i < speeds.Length; i++)
            {
                float start = speeds[i].startTime;
                float end = speeds[i].endTime;
                float value = speeds[i].value;
                if (start > time)
                {
                    index = i - 1;
                    break;
                }
                if (end > 0)
                {
                    if (start < 0)
                        num += (end - 0) * value;
                    else num += (end - start) * value;
                }
            }
            num -= (speeds[index].endTime - time) * speeds[index].value;
            return num;
        }

        public static float GetJudgelineSpeed(float time, SpeedEvent[] sp)
        {
            float result = 0;
            for (int i = 0; i < sp.Length; i++)
            {
                SpeedEvent sppp = sp[i];
                if (sppp.startTime < time && sppp.endTime > time) result = sppp.value;
            }
            return result;
        }

        public static float GetJudgelineSpeedV3(float time, SpeedEventV3[] sp)
        {
            float result = 0;
            for (int i = 0; i < sp.Length; i++)
            {
                SpeedEventV3 sppp = sp[i];
                if (sppp.startTime < time && sppp.endTime > time) result = sppp.value;
            }
            return result;
        }

        static void log(string msg)
        {
            Console.WriteLine(msg);
        }

        static int parseInt(string s)
        {
            return int.Parse(s);
        }

        static float parse(string s)
        {
            return float.Parse(s);
        }

        static void assert(bool b)
        {
            if (b) throw new Exception(b.GetHashCode().ToString());
        }

        static void error(params object[] msg)
        {
            string msgAdd = "";
            for (int i = 0; i < msg.Length; i++)
            {
                msgAdd += msg[i].ToString();
            }
            throw new Exception(msgAdd);
        }

        static int conv_time(string s)
        {
            return parseInt(s) * 32;
        }

        

        public static EaseUtility.Ease ParseEase(string s)
        {
            int type = int.Parse(s);
            switch (type)
            {
                case 1:
                    return EaseUtility.Ease.Linear;
                    break;
                case 2:
                    return EaseUtility.Ease.OutSine;
                    break;
                case 3:
                    return EaseUtility.Ease.InSine;
                    break;
                case 4:
                    return EaseUtility.Ease.OutQuad;
                    break;
                case 5:
                    return EaseUtility.Ease.InQuad;
                    break;
                case 6:
                    return EaseUtility.Ease.InOutSine;
                    break;
                case 7:
                    return EaseUtility.Ease.InOutQuad;
                    break;
                case 8:
                    return EaseUtility.Ease.OutCubic;
                    break;
                case 9:
                    return EaseUtility.Ease.InCubic;
                    break;
                case 10:
                    return EaseUtility.Ease.OutQuart;
                    break;
                case 11:
                    return EaseUtility.Ease.InQuart;
                    break;
                case 12:
                    return EaseUtility.Ease.InOutCubic;
                    break;
                case 13:
                    return EaseUtility.Ease.InOutQuart;
                    break;
                case 14:
                    return EaseUtility.Ease.OutQuart;
                    break;
                case 15:
                    return EaseUtility.Ease.InQuint;
                    break;
                case 16:
                    return EaseUtility.Ease.OutExpo;
                    break;
                case 17:
                    return EaseUtility.Ease.InExpo;
                    break;
                case 18:
                    return EaseUtility.Ease.OutCirc;
                    break;
                case 19:
                    return EaseUtility.Ease.InCirc;
                    break;
                case 20:
                    return EaseUtility.Ease.OutBack;
                    break;
                case 21:
                    return EaseUtility.Ease.InBack;
                    break;
                case 22:
                    return EaseUtility.Ease.InOutCirc;
                    break;
                case 23:
                    return EaseUtility.Ease.InOutBack;
                    break;
                case 24:
                    return EaseUtility.Ease.OutElastic;
                    break;
                case 25:
                    return EaseUtility.Ease.InElastic;
                    break;
                case 26:
                    return EaseUtility.Ease.OutBounce;
                    break;
                case 27:
                    return EaseUtility.Ease.InBounce;
                    break;
                case 28:
                    return EaseUtility.Ease.InOutBounce;
                    break;
                case 29:
                    return EaseUtility.Ease.InOutElastic;
                    break;

                default: return EaseUtility.Ease.Linear;
            }
        }

        public class valueSet : ValueSet, IComparable<valueSet>
        {
            public EaseUtility.Ease? easeType = null;
            public int CompareTo(valueSet other)
            {
                if (other.startTime > this.startTime)
                    return -1;
                else if (other.startTime < this.startTime)
                    return 1;
                else return 0;
            }
        }

        public class speedEvent : SpeedEvent, IComparable<speedEvent>
        {
            public int CompareTo(speedEvent other)
            {
                if (other.startTime > this.startTime)
                    return -1;
                else if (other.startTime < this.startTime)
                    return 1;
                else return 0;
            }
        }

        static List<valueSet> FillValues(List<valueSet> vs, float bpm)
        {
            vs.Sort();
            if (vs.Count == 0)
                return vs;
            if (vs.Count == 1)
                return vs;
            if (vs.Count > 1)
            {
                for (int i = 0; i < vs.Count; i++)
                {
                    if (i == 0)
                        continue;
                    if (i > 100000)
                    {
                        throw new OutOfMemoryException();
                        break;
                    }
                    if (vs[i - 1].endTime != vs[i].startTime)
                    {
                        vs.Insert(i, new valueSet
                        {
                            startTime = vs[i - 1].endTime,
                            endTime = vs[i].startTime,
                            start = vs[i - 1].end,
                            start2 = vs[i - 1].end2,
                            end = vs[i - 1].end,
                            end2 = vs[i - 1].end2,
                        });
                    }
                }
            }
            return vs;
        }

        static List<valueSet> EaseValues(List<valueSet> vs, float bpm)
        {
            vs.Sort();
            if (vs.Count == 0)
                return vs;
            if (vs.Count == 1)
                return vs;
            if (vs.Count > 1)
            {
                for (int i = 0; i < vs.Count; i++)
                {
                    if (i == 0)
                        continue;
                    if (i > 100000)
                    {
                        throw new OutOfMemoryException();
                        break;
                    }
                    if (vs[i].easeType != null)
                    {
                        if (i >= vs.Count) break;
                        valueSet vss = vs[i];
                        vss.start = vs[i - 1].end;
                        vss.start2 = vs[i - 1].end2;
                        List<valueSet> qwq = new List<valueSet>();
                        vs.Remove(vs[i]);
                        int startTime = Utils.GetBeatTimeFromTime(vss.startTime, bpm);
                        int endTime = Utils.GetBeatTimeFromTime(vss.endTime, bpm);
                        for (int j = 0; j < endTime - startTime - 1; j++)
                        {
                            float nt = j / (float)(endTime - startTime);
                            float ease = EaseUtility.Evaluate(vss.easeType.Value, j - 1, (endTime - startTime));
                            float easeNext = EaseUtility.Evaluate(vss.easeType.Value, j, (endTime - startTime));
                            qwq.Add(new valueSet
                            {
                                startTime = (float)Utils.GetTimeFromBeatTime(j + startTime - 1, bpm),
                                endTime = (float)Utils.GetTimeFromBeatTime(j + startTime, bpm),
                                start = vss.start + (vss.end - vss.start) * ease,
                                start2 = vss.start2 + (vss.end2 - vss.start2) * ease,
                                end = vss.start + (vss.end - vss.start) * easeNext,
                                end2 = vss.start2 + (vss.end2 - vss.start2) * easeNext,
                                easeType = null
                            });
                        }
                        float last1;
                        float last2;
                        try
                        {
                            last1 = qwq[qwq.Count - 1].end;
                            last2 = qwq[qwq.Count - 1].end2;
                        }
                        catch
                        {
                            last1 = vss.end;
                            last2 = vss.end2;
                        }
                        qwq.Add(new valueSet
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(endTime - 1, bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime(endTime, bpm),
                            start = last1,
                            start2 = last2,
                            end = vss.end,
                            end2 = vss.end2,
                            easeType = null
                        });
                        vs.InsertRange(i, qwq);
                    }
                }
            }
            return vs;
        }

        static string ToPPC(string pec)
        {
            Chart chart = new Chart();
            chart.chartVersion = "PEC";
            chart.offset = 0;
            chart.judgeLineList = new JudgeLineSet[100];//最多支持100条判定线
            int judgelineCount = 0;
            string[] lines = pec.Split('\n');
            for (int i = 0; i < 100; i++)
            {
                chart.judgeLineList[i] = new JudgeLineSet();
                chart.judgeLineList[i].bpm = float.Parse(lines[1].Split(' ')[2]);
                chart.judgeLineList[i].rotateEvents = new valueSet[0];
                chart.judgeLineList[i].alphaEvents = new valueSet[0];
                chart.judgeLineList[i].moveEvents = new valueSet[0];
                chart.judgeLineList[i].speedEvents = new SpeedEvent[1];
                chart.judgeLineList[i].speedEvents[0] = new SpeedEvent()
                {
                    startTime = 0,
                    endTime = 99999,
                    value = 1
                };
            }
            float bpm = float.Parse(lines[1].Split(' ')[2]);
            Debug.Log(lines[1].Split(' ')[2]);
            int noteCount = 0;
            List<List<Note.NoteData>> aboves = new List<List<Note.NoteData>>();
            List<List<Note.NoteData>> belows = new List<List<Note.NoteData>>();
            List<List<valueSet>> moves = new List<List<valueSet>>();
            List<List<valueSet>> rotates = new List<List<valueSet>>();
            List<List<valueSet>> alphas = new List<List<valueSet>>();
            List<List<speedEvent>> speeds = new List<List<speedEvent>>();
            for (int i = 0; i < 100; i++)//初始化
            {
                aboves.Add(new List<Note.NoteData>());
                belows.Add(new List<Note.NoteData>());
                moves.Add(new List<valueSet>());
                rotates.Add(new List<valueSet>());
                alphas.Add(new List<valueSet>());
                speeds.Add(new List<speedEvent>());
            }
            foreach (string line in lines)
            {
                if (line.Contains("#") || line.Contains("&"))//忽略大小
                    continue;
                try
                {
                    if (line.StartsWith("n") || line.StartsWith("c"))
                    {
                        if (int.Parse(line.Split(' ')[1].Trim()) > judgelineCount)
                            judgelineCount = int.Parse(line.Split(' ')[1].Trim());
                    }
                }
                catch { }
                try
                {
                    if (line.StartsWith("n"))//Note
                    {
                        char t = line.ToCharArray()[1];
                        NoteType tp = NoteType.Click;
                        switch (t)
                        {
                            case '1': tp = NoteType.Click; break;
                            case '2': tp = NoteType.Hold; break;
                            case '3': tp = NoteType.Flick; break;
                            case '4': tp = NoteType.Drag; break;
                        }
                        int dir = (int)(float.Parse(line.Split(' ')[4].Trim()));
                        Note.NoteData ns = new Note.NoteData()
                        {
                            type = tp,
                            beatTime = (int)(float.Parse(line.Split(' ')[2].Trim()) * 32),
                            positionX = float.Parse(line.Split(' ')[3].Trim()) / 1024 * 8.9f,
                            speed = 12,
                            beatHoldTime = 0
                        };
                        if (tp == NoteType.Hold)
                        {
                            ns = new Note.NoteData()
                            {
                                type = tp,
                                beatTime = (int)(float.Parse(line.Split(' ')[2].Trim()) * 32),
                                positionX = float.Parse(line.Split(' ')[4].Trim()) / 1024 * 8.9f,
                                speed = 12,
                                beatHoldTime = (int)(float.Parse(line.Split(' ')[3].Trim()) * 32) - (int)(float.Parse(line.Split(' ')[2].Trim()) * 32)
                            };
                            dir = (int)(float.Parse(line.Split(' ')[5].Trim()));
                        }
                        if (!UseFakeNotes)
                        {
                            if (tp != NoteType.Hold)
                            {
                                if ((int.Parse(line.Split(' ')[5].Trim())) == 1)
                                {
                                    Utils.AndroidToastStringShow("Fake notes is ignored.");
                                    continue;
                                }
                            }
                            if (tp == NoteType.Hold)
                            {
                                if ((int.Parse(line.Split(' ')[6].Trim())) == 1)
                                {
                                    Utils.AndroidToastStringShow("Fake notes is ignored.");
                                    continue;
                                }
                            }
                        }
                        else
                        {
                            if (tp != NoteType.Hold)
                            {
                                if ((int.Parse(line.Split(' ')[5].Trim())) == 1)
                                {
                                    ns.speed = 0;
                                }
                            }
                            if (tp == NoteType.Hold)
                            {
                                if ((int.Parse(line.Split(' ')[6].Trim())) == 1)
                                {
                                    ns.speed = 0;
                                }
                            }
                        }
                        if (dir == 1) aboves[(int.Parse(line.Split(' ')[1].Trim()))].Add(ns);
                        else belows[(int.Parse(line.Split(' ')[1].Trim()))].Add(ns);
                        noteCount++;
                    }
                    else if (line.StartsWith("cv"))//velocity
                    {
                        int index = int.Parse(line.Split(' ')[1]);
                        speedEvent se = new speedEvent()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            endTime = 0,
                            value = float.Parse(line.Split(' ')[3]) / 7.0f
                        };
                        int ssss = speeds[index].Count;
                        speeds[index].Add(se);
                    }
                    else if (line.StartsWith("cp"))//move
                    {
                        int index = int.Parse(line.Split(' ')[1]);
                        if (index >= 100) index = 100;
                        valueSet v = new valueSet()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            start = ((float.Parse(line.Split(' ')[3]) / 2048f) - 0.5f) * 18,
                            start2 = ((float.Parse(line.Split(' ')[4]) / 1400f) - 0.5f) * 10,
                            end = ((float.Parse(line.Split(' ')[3]) / 2048f) - 0.5f) * 18,
                            end2 = ((float.Parse(line.Split(' ')[4]) / 1400f) - 0.5f) * 10,
                            easeType = null
                        };
                        moves[index].Add(v);
                    }
                    else if (line.StartsWith("cm"))//move
                    {
                        int index = int.Parse(line.Split(' ')[1]);
                        if (index >= 100) index = 100;
                        moves[index].Sort();

                        valueSet v = new valueSet()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime((float.Parse(line.Split(' ')[2]) * 32), bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime((float.Parse(line.Split(' ')[3]) * 32), bpm),
                            end = ((float.Parse(line.Split(' ')[4]) / 2048f) - 0.5f) * 18,
                            end2 = ((float.Parse(line.Split(' ')[5]) / 1400f) - 0.5f) * 10,
                            start = float.NaN,
                            start2 = float.NaN,
                            easeType = ParseEase(line.Split(' ')[6])
                        };
                        moves[index].Add(v);
                    }
                    else if (line.StartsWith("cf"))//alpha
                    {

                        int index = int.Parse(line.Split(' ')[1]);
                        if (index >= 100) index = 100;
                        valueSet v = new valueSet()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[3]) * 32, bpm),
                            start = float.NaN,
                            end = float.Parse(line.Split(' ')[4]) / 255f,
                            easeType = null
                        };
                        alphas[index].Add(v);
                    }
                    else if (line.StartsWith("ca"))//alpha
                    {
                        int index = int.Parse(line.Split(' ')[1]);
                        if (index >= 100) index = 100;
                        valueSet v = new valueSet()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            end = float.Parse(line.Split(' ')[3]) / 255f,
                            start = float.Parse(line.Split(' ')[3]) / 255f,
                            easeType = null
                        };
                        alphas[index].Add(v);
                    }

                    else if (line.StartsWith("cr"))//rotate
                    {
                        int index = int.Parse(line.Split(' ')[1]);
                        if (index >= 100) index = 100;
                        valueSet v = new valueSet()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[3]) * 32, bpm),
                            start = float.NaN,
                            end = -float.Parse(line.Split(' ')[4]),
                            easeType = ParseEase(line.Split(' ')[5])
                        };
                        rotates[index].Add(v);
                    }
                    else if (line.StartsWith("cd"))//rotate
                    {
                        int index = int.Parse(line.Split(' ')[1]);
                        if (index >= 100) index = 100;
                        valueSet v = new valueSet()
                        {
                            startTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            endTime = (float)Utils.GetTimeFromBeatTime(float.Parse(line.Split(' ')[2]) * 32, bpm),
                            start = -float.Parse(line.Split(' ')[3]),
                            end = -float.Parse(line.Split(' ')[3]),
                            easeType = null
                        };
                        rotates[index].Add(v);
                    }
#if !UNITY_EDITOR
                }
                catch (Exception e)
                {
                    Utils.AndroidToastStringShow("Read error at line : " + line + ",Ex : " + e.Message);
                }
#endif
            }

            chart.numOfNotes = noteCount;
            chart.judgeLineList = new JudgeLineSet[judgelineCount + 1];
            Debug.Log(judgelineCount);
            for (int i = 0; i < judgelineCount + 1; i++)
            {
                chart.judgeLineList[i] = new JudgeLineSet();
                chart.judgeLineList[i].bpm = float.Parse(lines[1].Split(' ')[2]);
                chart.judgeLineList[i].rotateEvents = new valueSet[0];
                chart.judgeLineList[i].alphaEvents = new valueSet[0];
                chart.judgeLineList[i].moveEvents = new valueSet[0];
                chart.judgeLineList[i].speedEvents = new speedEvent[0];
            }
            Debug.Log($"judgelineCount+1={judgelineCount + 1}");
            for (int i = 0; i < judgelineCount + 1; i++)
            {//设置数据
                chart.judgeLineList[i].notesAbove = aboves[i].ToArray();
                chart.judgeLineList[i].notesBelow = belows[i].ToArray();
                speeds[i].Sort();
                moves[i].Sort();
                rotates[i].Sort();
                alphas[i].Sort();
#if !UNITY_EDITOR
                try
                {
#endif
                    //设置eventStart数值
                    List<valueSet> ms = moves[i];
                    List<valueSet> rs = rotates[i];
                    List<valueSet> as1 = alphas[i];
                    List<speedEvent> ses = speeds[i];
#if !UNITY_EDITOR
                    try
                    {
#endif
                        //设置缓动
                        if (true)
                        {
                            ms = EaseValues(ms, bpm);
                            rs = EaseValues(rs, bpm);
                            as1 = EaseValues(as1, bpm);
                        }
                        //设置eventStart数值
                        for (int j = 0; j < ms.Count; j++)
                        {
                            if (j == 0)
                            {
                                ms[0].start = ms[0].end;
                                ms[0].start2 = ms[0].end2;
                                continue;
                            }
                            valueSet v = ms[j];
                            if (float.IsNaN(v.start)) v.start = ms[j - 1].end;
                            if (float.IsNaN(v.start2)) v.start2 = ms[j - 1].end2;
                            ms[j] = v;
                        }
                        if (ms.Count == 0)
                            ms.Add(new valueSet { startTime = 0, endTime = 9999, start = 0, end = 0, start2 = 0, end2 = 0 });
                        else if (ms.Count == 1)
                        {
                            ms[0].endTime = 9999;
                        }
                        else ms.Add(new valueSet { startTime = ms[ms.Count - 1].endTime, endTime = 9999, start = ms[ms.Count - 1].end, end = ms[ms.Count - 1].end, start2 = ms[ms.Count - 1].end2, end2 = ms[ms.Count - 1].end2 });

                        for (int j = 0; j < rs.Count; j++)
                        {
                            if (j == 0)
                            {
                                rs[0].start = rs[0].end;
                                rs[0].start2 = rs[0].end2;
                                continue;
                            }
                            valueSet v = rs[j];
                            if (float.IsNaN(v.start)) v.start = rs[j - 1].end;
                            v.start2 = rs[j - 1].end2;
                            rs[j] = v;
                        }
                        if (rs.Count == 0)
                            rs.Add(new valueSet { startTime = 0, endTime = 9999, start = 0, end = 0, start2 = 0, end2 = 0 });
                        else if (rs.Count == 1)
                        {
                            rs[0].endTime = 9999;
                        }
                        else rs.Add(new valueSet { startTime = rs[rs.Count - 1].endTime, endTime = 9999, start = rs[rs.Count - 1].end, end = rs[rs.Count - 1].end, start2 = rs[rs.Count - 1].end2, end2 = rs[rs.Count - 1].end2 });

                        for (int j = 0; j < as1.Count; j++)
                        {
                            if (j == 0)
                            {
                                as1[0].start = as1[0].end;
                                as1[0].start2 = as1[0].end2;
                                continue;
                            }
                            valueSet v = as1[j];
                            if (float.IsNaN(v.start)) v.start = as1[j - 1].end;
                            v.start2 = as1[j - 1].end2;
                            as1[j] = v;
                        }
                        if (as1.Count == 0)
                            as1.Add(new valueSet { startTime = 0, endTime = 9999, start = 0, end = 0, start2 = 0, end2 = 0 });
                        else if (as1.Count == 1)
                        {
                            as1[0].endTime = 9999;
                        }
                        else as1.Add(new valueSet { startTime = as1[as1.Count - 1].endTime, endTime = 9999, start = as1[as1.Count - 1].end, end = as1[as1.Count - 1].end, start2 = as1[as1.Count - 1].end2, end2 = as1[as1.Count - 1].end2 });

                        for (int j = 0; j < ses.Count - 1; j++)
                        {
                            if (ses.Count == 0 || ses.Count == 1) break;//assert
                            speedEvent v = ses[j];
                            v.endTime = ses[j + 1].startTime;
                            ses[j] = v;
                        }
                        if (ses.Count == 1)
                            ses[0].value = 0;
                        if (ses.Count == 0)
                            ses.Add(new speedEvent { startTime = 0, endTime = 9999, value = 0 });
                        else if (ses.Count == 1)
                        {
                            ses[0].endTime = 9999;
                        }
                        else ses.Add(new speedEvent { startTime = ses[ses.Count - 1].endTime, endTime = 9999, value = ses[ses.Count - 1].value });
                    }
                    catch (Exception e) { Utils.AndroidToastStringShow(e.Message); }

                    ms = FillValues(ms, bpm);
                    rs = FillValues(rs, bpm);
                    as1 = FillValues(as1, bpm);
                    // ms = EaseValues(ms,bpm);
                    chart.judgeLineList[i].moveEvents = ms.ToArray();
                    chart.judgeLineList[i].rotateEvents = rs.ToArray();
                    chart.judgeLineList[i].alphaEvents = as1.ToArray();
                    chart.judgeLineList[i].speedEvents = ses.ToArray();
                }
                catch (Exception e)
                {
                    Utils.AndroidToastStringShow(e.Message);
                }
            }

            return JsonConvert.SerializeObject(chart, Formatting.Indented);

        }
    }

    public class Utils
    {
        public static double GetTimeFromBeatTime(float beatTime, float bpm)
        {
            double time = beatTime * (60 / bpm) / 32;
            if (time > 1024) time = 1024;//限制数值大小，避免出现错误（音乐不可能超过17.67分钟）
            return time;
        }

        public static int GetBeatTimeFromTime(double time, float bpm)
        {
            int btime = (int)Math.Round((float)time * 32 / (60 / bpm));
            if (btime > 32767) btime = 32767;//限制数值大小，避免出现错误（同时防止bpm为0错误）
            return btime;
        }

        public static void AndroidToastStringShow(object str)
        {
            Console.WriteLine(str);
        }
    }

    public enum NoteType
    {
        Click, Drag, Hold, Flick
    }

    [Serializable]
    public class PhiChart
    {
        public int formatVersion;
        public float offset;
        public int numOfNotes;
        public PhiJudgeLineSet[] judgeLineList;
    }

    [Serializable]
    public class Chart
    {
        public string chartVersion;
        public float offset;
        public int numOfNotes;
        public JudgeLineSet[] judgeLineList;
    }

    [Serializable]
    public class JudgeLineSet
    {
        public float bpm;
        public SpeedEvent[] speedEvents;
        public Note.NoteData[] notesAbove;
        public Note.NoteData[] notesBelow;
        public ValueSet[] alphaEvents;
        public ValueSet[] moveEvents;
        public ValueSet[] rotateEvents;
    }

    [Serializable]
    public class ValueSet
    {
        public float startTime;
        public float endTime;
        public float start;
        public float end;
        public float start2;
        public float end2;
    }

    [Serializable]
    public class SpeedEvent
    {
        public float startTime;
        public float endTime;
        public float value;
    }

    [Serializable]
    public class SpeedEventV3
    {
        public float startTime;
        public float endTime;
        public float value;
        public float floorPosition;
    }

    [Serializable]
    public class PhiJudgeLineSet
    {
        public int numOfNotes;
        public int numOfNotesAbove;
        public int numOfNotesBelow;
        public float bpm;
        public List<SpeedEventV3> speedEvents = new List<SpeedEventV3>();
        public List<NoteSet> notesAbove = new List<NoteSet>();
        public List<NoteSet> notesBelow = new List<NoteSet>();
        public List<ValueSet> judgeLineDisappearEvents = new List<ValueSet>();
        public List<ValueSet> judgeLineMoveEvents = new List<ValueSet>();
        public List<ValueSet> judgeLineRotateEvents = new List<ValueSet>();
    }

    [Serializable]
    public class NoteSet
    {
        public int type;
        public int time;
        public float positionX;
        public float holdTime;
        public float speed;
        public float floorPosition;
        public bool isJudged;
        public bool isJudgedForFlick;
        public float realTime;
        public int judgeLineIndex;
        public int noteIndex;
    }

    [Serializable]
    public class Note
    {
        [Serializable]
        public class NoteData
        {
            public NoteType type;
            public float positionX;
            public float speed;
            public int beatTime;
            public int beatHoldTime;
        }
    }

    public class Debug
    {
        public static void Log(object msg)
        {
            Console.WriteLine("Log : "+msg);
        }

        public static void LogWarning(object msg)
        {
            Console.WriteLine("Warning : "+msg);
        }

        public static void LogError(object msg)
        {
            Console.WriteLine("Error : "+msg);
        }
    }

    /// <summary>
    /// This code is from Dotween
    /// </summary>
    public class EaseUtility
    {
        public enum Ease
        {
            Linear,
            InSine,
            OutSine,
            InOutSine,
            InQuad,
            OutQuad,
            InOutQuad,
            InCubic,
            OutCubic,
            InOutCubic,
            InQuart,
            OutQuart,
            InOutQuart,
            InQuint,
            OutQuint,
            InOutQuint,
            InExpo,
            OutExpo,
            InOutExpo,
            InCirc,
            OutCirc,
            InOutCirc,
            InElastic,
            OutElastic,
            InOutElastic,
            InBack,
            OutBack,
            InOutBack,
            InBounce,
            OutBounce,
            InOutBounce,
            Flash,
            InFlash,
            OutFlash,
            InOutFlash
        }

        public static float Evaluate(Ease easeType, float time, float duration, float overshootOrAmplitude = 0, float period = 0)
        {
            switch (easeType)
            {
                case Ease.Linear:
                    return time / duration;
                case Ease.InSine:
                    return -(float)Math.Cos((double)(time / duration * 1.57079637f)) + 1f;
                case Ease.OutSine:
                    return (float)Math.Sin((double)(time / duration * 1.57079637f));
                case Ease.InOutSine:
                    return -0.5f * ((float)Math.Cos((double)(3.14159274f * time / duration)) - 1f);
                case Ease.InQuad:
                    return (time /= duration) * time;
                case Ease.OutQuad:
                    return -(time /= duration) * (time - 2f);
                case Ease.InOutQuad:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time;
                    }
                    return -0.5f * ((time -= 1f) * (time - 2f) - 1f);
                case Ease.InCubic:
                    return (time /= duration) * time * time;
                case Ease.OutCubic:
                    return (time = time / duration - 1f) * time * time + 1f;
                case Ease.InOutCubic:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time * time;
                    }
                    return 0.5f * ((time -= 2f) * time * time + 2f);
                case Ease.InQuart:
                    return (time /= duration) * time * time * time;
                case Ease.OutQuart:
                    return -((time = time / duration - 1f) * time * time * time - 1f);
                case Ease.InOutQuart:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time * time * time;
                    }
                    return -0.5f * ((time -= 2f) * time * time * time - 2f);
                case Ease.InQuint:
                    return (time /= duration) * time * time * time * time;
                case Ease.OutQuint:
                    return (time = time / duration - 1f) * time * time * time * time + 1f;
                case Ease.InOutQuint:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * time * time * time * time * time;
                    }
                    return 0.5f * ((time -= 2f) * time * time * time * time + 2f);
                case Ease.InExpo:
                    if (time != 0f)
                    {
                        return (float)Math.Pow(2.0, (double)(10f * (time / duration - 1f)));
                    }
                    return 0f;
                case Ease.OutExpo:
                    if (time == duration)
                    {
                        return 1f;
                    }
                    return -(float)Math.Pow(2.0, (double)(-10f * time / duration)) + 1f;
                case Ease.InOutExpo:
                    if (time == 0f)
                    {
                        return 0f;
                    }
                    if (time == duration)
                    {
                        return 1f;
                    }
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * (float)Math.Pow(2.0, (double)(10f * (time - 1f)));
                    }
                    return 0.5f * (-(float)Math.Pow(2.0, (double)(-10f * (time -= 1f))) + 2f);
                case Ease.InCirc:
                    return -((float)Math.Sqrt((double)(1f - (time /= duration) * time)) - 1f);
                case Ease.OutCirc:
                    return (float)Math.Sqrt((double)(1f - (time = time / duration - 1f) * time));
                case Ease.InOutCirc:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return -0.5f * ((float)Math.Sqrt((double)(1f - time * time)) - 1f);
                    }
                    return 0.5f * ((float)Math.Sqrt((double)(1f - (time -= 2f) * time)) + 1f);
                case Ease.InElastic:
                    {
                        if (time == 0f)
                        {
                            return 0f;
                        }
                        if ((time /= duration) == 1f)
                        {
                            return 1f;
                        }
                        if (period == 0f)
                        {
                            period = duration * 0.3f;
                        }
                        float num;
                        if (overshootOrAmplitude < 1f)
                        {
                            overshootOrAmplitude = 1f;
                            num = period / 4f;
                        }
                        else
                        {
                            num = period / 6.28318548f * (float)Math.Asin((double)(1f / overshootOrAmplitude));
                        }
                        return -(overshootOrAmplitude * (float)Math.Pow(2.0, (double)(10f * (time -= 1f))) * (float)Math.Sin((double)((time * duration - num) * 6.28318548f / period)));
                    }
                case Ease.OutElastic:
                    {
                        if (time == 0f)
                        {
                            return 0f;
                        }
                        if ((time /= duration) == 1f)
                        {
                            return 1f;
                        }
                        if (period == 0f)
                        {
                            period = duration * 0.3f;
                        }
                        float num2;
                        if (overshootOrAmplitude < 1f)
                        {
                            overshootOrAmplitude = 1f;
                            num2 = period / 4f;
                        }
                        else
                        {
                            num2 = period / 6.28318548f * (float)Math.Asin((double)(1f / overshootOrAmplitude));
                        }
                        return overshootOrAmplitude * (float)Math.Pow(2.0, (double)(-10f * time)) * (float)Math.Sin((double)((time * duration - num2) * 6.28318548f / period)) + 1f;
                    }
                case Ease.InOutElastic:
                    {
                        if (time == 0f)
                        {
                            return 0f;
                        }
                        if ((time /= duration * 0.5f) == 2f)
                        {
                            return 1f;
                        }
                        if (period == 0f)
                        {
                            period = duration * 0.450000018f;
                        }
                        float num3;
                        if (overshootOrAmplitude < 1f)
                        {
                            overshootOrAmplitude = 1f;
                            num3 = period / 4f;
                        }
                        else
                        {
                            num3 = period / 6.28318548f * (float)Math.Asin((double)(1f / overshootOrAmplitude));
                        }
                        if (time < 1f)
                        {
                            return -0.5f * (overshootOrAmplitude * (float)Math.Pow(2.0, (double)(10f * (time -= 1f))) * (float)Math.Sin((double)((time * duration - num3) * 6.28318548f / period)));
                        }
                        return overshootOrAmplitude * (float)Math.Pow(2.0, (double)(-10f * (time -= 1f))) * (float)Math.Sin((double)((time * duration - num3) * 6.28318548f / period)) * 0.5f + 1f;
                    }
                case Ease.InBack:
                    return (time /= duration) * time * ((overshootOrAmplitude + 1f) * time - overshootOrAmplitude);
                case Ease.OutBack:
                    return (time = time / duration - 1f) * time * ((overshootOrAmplitude + 1f) * time + overshootOrAmplitude) + 1f;
                case Ease.InOutBack:
                    if ((time /= duration * 0.5f) < 1f)
                    {
                        return 0.5f * (time * time * (((overshootOrAmplitude *= 1.525f) + 1f) * time - overshootOrAmplitude));
                    }
                    return 0.5f * ((time -= 2f) * time * (((overshootOrAmplitude *= 1.525f) + 1f) * time + overshootOrAmplitude) + 2f);
                case Ease.InBounce:
                    return Bounce.EaseIn(time, duration, overshootOrAmplitude, period);
                case Ease.OutBounce:
                    return Bounce.EaseOut(time, duration, overshootOrAmplitude, period);
                case Ease.InOutBounce:
                    return Bounce.EaseInOut(time, duration, overshootOrAmplitude, period);
                case Ease.Flash:
                    return Flash.Ease(time, duration, overshootOrAmplitude, period);
                case Ease.InFlash:
                    return Flash.EaseIn(time, duration, overshootOrAmplitude, period);
                case Ease.OutFlash:
                    return Flash.EaseOut(time, duration, overshootOrAmplitude, period);
                case Ease.InOutFlash:
                    return Flash.EaseInOut(time, duration, overshootOrAmplitude, period);
                default:
                    return -(time /= duration) * (time - 2f);
            }
        }

        /// <summary>
        /// This class contains a C# port of the easing equations created by Robert Penner (http://robertpenner.com/easing).
        /// </summary>
        public static class Bounce
        {
            /// <summary>
            /// Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in: accelerating from zero velocity.
            /// </summary>
            /// <param name="time">
            /// Current time (in frames or seconds).
            /// </param>
            /// <param name="duration">
            /// Expected easing duration (in frames or seconds).
            /// </param>
            /// <param name="unusedOvershootOrAmplitude">Unused: here to keep same delegate for all ease types.</param>
            /// <param name="unusedPeriod">Unused: here to keep same delegate for all ease types.</param>
            /// <returns>
            /// The eased value.
            /// </returns>
            public static float EaseIn(float time, float duration, float unusedOvershootOrAmplitude, float unusedPeriod)
            {
                return 1f - Bounce.EaseOut(duration - time, duration, -1f, -1f);
            }

            /// <summary>
            /// Easing equation function for a bounce (exponentially decaying parabolic bounce) easing out: decelerating from zero velocity.
            /// </summary>
            /// <param name="time">
            /// Current time (in frames or seconds).
            /// </param>
            /// <param name="duration">
            /// Expected easing duration (in frames or seconds).
            /// </param>
            /// <param name="unusedOvershootOrAmplitude">Unused: here to keep same delegate for all ease types.</param>
            /// <param name="unusedPeriod">Unused: here to keep same delegate for all ease types.</param>
            /// <returns>
            /// The eased value.
            /// </returns>
            public static float EaseOut(float time, float duration, float unusedOvershootOrAmplitude, float unusedPeriod)
            {
                if ((time /= duration) < 0.363636374f)
                {
                    return 7.5625f * time * time;
                }
                if (time < 0.727272749f)
                {
                    return 7.5625f * (time -= 0.545454562f) * time + 0.75f;
                }
                if (time < 0.909090936f)
                {
                    return 7.5625f * (time -= 0.8181818f) * time + 0.9375f;
                }
                return 7.5625f * (time -= 0.954545438f) * time + 0.984375f;
            }

            /// <summary>
            /// Easing equation function for a bounce (exponentially decaying parabolic bounce) easing in/out: acceleration until halfway, then deceleration.
            /// </summary>
            /// <param name="time">
            /// Current time (in frames or seconds).
            /// </param>
            /// <param name="duration">
            /// Expected easing duration (in frames or seconds).
            /// </param>
            /// <param name="unusedOvershootOrAmplitude">Unused: here to keep same delegate for all ease types.</param>
            /// <param name="unusedPeriod">Unused: here to keep same delegate for all ease types.</param>
            /// <returns>
            /// The eased value.
            /// </returns>
            public static float EaseInOut(float time, float duration, float unusedOvershootOrAmplitude, float unusedPeriod)
            {
                if (time < duration * 0.5f)
                {
                    return Bounce.EaseIn(time * 2f, duration, -1f, -1f) * 0.5f;
                }
                return Bounce.EaseOut(time * 2f - duration, duration, -1f, -1f) * 0.5f + 0.5f;
            }
        }

        public static class Flash
        {
            public static float Ease(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = (int)Math.Ceiling(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                float res = time * num3 / num2;
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            public static float EaseIn(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                time *= num3;
                float res = (time /= num2) * time;
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            public static float EaseOut(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                time *= num3;
                float res = -(time /= num2) * (time - 2f);
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            public static float EaseInOut(float time, float duration, float overshootOrAmplitude, float period)
            {
                int num = Mathf.CeilToInt(time / duration * overshootOrAmplitude);
                float num2 = duration / overshootOrAmplitude;
                time -= num2 * (float)(num - 1);
                float num3 = (float)((num % 2 != 0) ? 1 : -1);
                if (num3 < 0f)
                {
                    time -= num2;
                }
                time *= num3;
                float res = ((time /= num2 * 0.5f) < 1f) ? (0.5f * time * time) : (-0.5f * ((time -= 1f) * (time - 2f) - 1f));
                return Flash.WeightedEase(overshootOrAmplitude, period, num, num2, num3, res);
            }

            private static float WeightedEase(float overshootOrAmplitude, float period, int stepIndex, float stepDuration, float dir, float res)
            {
                float num = 0f;
                float num2 = 0f;
                if (dir > 0f && (int)overshootOrAmplitude % 2 == 0)
                {
                    stepIndex++;
                }
                else if (dir < 0f && (int)overshootOrAmplitude % 2 != 0)
                {
                    stepIndex++;
                }
                if (period > 0f)
                {
                    float num3 = (float)Math.Truncate((double)overshootOrAmplitude);
                    num2 = overshootOrAmplitude - num3;
                    if (num3 % 2f > 0f)
                    {
                        num2 = 1f - num2;
                    }
                    num2 = num2 * (float)stepIndex / overshootOrAmplitude;
                    num = res * (overshootOrAmplitude - (float)stepIndex) / overshootOrAmplitude;
                }
                else if (period < 0f)
                {
                    period = -period;
                    num = res * (float)stepIndex / overshootOrAmplitude;
                }
                float num4 = num - res;
                res += num4 * period + num2;
                if (res > 1f)
                {
                    res = 1f;
                }
                return res;
            }
        }
    }

    /// <summary>
    /// This code is from UnityEngine.Mathf
    /// </summary>
    public struct Mathf
    {
        /// <summary>
        ///   <para>Returns the sine of angle f.</para>
        /// </summary>
        /// <param name="f">The input angle, in radians.</param>
        /// <returns>
        ///   <para>The return value between -1 and +1.</para>
        /// </returns>
        public static float Sin(float f)
        {
            return (float)Math.Sin((double)f);
        }

        /// <summary>
        ///   <para>Returns the cosine of angle f.</para>
        /// </summary>
        /// <param name="f">The input angle, in radians.</param>
        /// <returns>
        ///   <para>The return value between -1 and 1.</para>
        /// </returns>
        public static float Cos(float f)
        {
            return (float)Math.Cos((double)f);
        }

        /// <summary>
        ///   <para>Returns the tangent of angle f in radians.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Tan(float f)
        {
            return (float)Math.Tan((double)f);
        }

        /// <summary>
        ///   <para>Returns the arc-sine of f - the angle in radians whose sine is f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Asin(float f)
        {
            return (float)Math.Asin((double)f);
        }

        /// <summary>
        ///   <para>Returns the arc-cosine of f - the angle in radians whose cosine is f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Acos(float f)
        {
            return (float)Math.Acos((double)f);
        }

        /// <summary>
        ///   <para>Returns the arc-tangent of f - the angle in radians whose tangent is f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Atan(float f)
        {
            return (float)Math.Atan((double)f);
        }

        /// <summary>
        ///   <para>Returns the angle in radians whose Tan is y/x.</para>
        /// </summary>
        /// <param name="y"></param>
        /// <param name="x"></param>
        public static float Atan2(float y, float x)
        {
            return (float)Math.Atan2((double)y, (double)x);
        }

        /// <summary>
        ///   <para>Returns square root of f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Sqrt(float f)
        {
            return (float)Math.Sqrt((double)f);
        }

        /// <summary>
        ///   <para>Returns the absolute value of f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Abs(float f)
        {
            return Math.Abs(f);
        }

        /// <summary>
		///   <para>Returns the smallest integer greater to or equal to f.</para>
		/// </summary>
		/// <param name="f"></param>
		public static int CeilToInt(float f)
        {
            return (int)Math.Ceiling((double)f);
        }

        /// <summary>
        ///   <para>Returns the absolute value of value.</para>
        /// </summary>
        /// <param name="value"></param>
        public static int Abs(int value)
        {
            return Math.Abs(value);
        }

        /// <summary>
        ///   <para>Returns the smallest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static float Min(float a, float b)
        {
            return (a < b) ? a : b;
        }

        /// <summary>
        ///   <para>Returns the smallest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static float Min(params float[] values)
        {
            int num = values.Length;
            bool flag = num == 0;
            float result;
            if (flag)
            {
                result = 0f;
            }
            else
            {
                float num2 = values[0];
                for (int i = 1; i < num; i++)
                {
                    bool flag2 = values[i] < num2;
                    if (flag2)
                    {
                        num2 = values[i];
                    }
                }
                result = num2;
            }
            return result;
        }

        /// <summary>
        ///   <para>Returns the smallest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static int Min(int a, int b)
        {
            return (a < b) ? a : b;
        }

        /// <summary>
        ///   <para>Returns the smallest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static int Min(params int[] values)
        {
            int num = values.Length;
            bool flag = num == 0;
            int result;
            if (flag)
            {
                result = 0;
            }
            else
            {
                int num2 = values[0];
                for (int i = 1; i < num; i++)
                {
                    bool flag2 = values[i] < num2;
                    if (flag2)
                    {
                        num2 = values[i];
                    }
                }
                result = num2;
            }
            return result;
        }

        /// <summary>
        ///   <para>Returns largest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static float Max(float a, float b)
        {
            return (a > b) ? a : b;
        }

        /// <summary>
        ///   <para>Returns largest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static float Max(params float[] values)
        {
            int num = values.Length;
            bool flag = num == 0;
            float result;
            if (flag)
            {
                result = 0f;
            }
            else
            {
                float num2 = values[0];
                for (int i = 1; i < num; i++)
                {
                    bool flag2 = values[i] > num2;
                    if (flag2)
                    {
                        num2 = values[i];
                    }
                }
                result = num2;
            }
            return result;
        }

        /// <summary>
        ///   <para>Returns the largest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static int Max(int a, int b)
        {
            return (a > b) ? a : b;
        }

        /// <summary>
        ///   <para>Returns the largest of two or more values.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="values"></param>
        public static int Max(params int[] values)
        {
            int num = values.Length;
            bool flag = num == 0;
            int result;
            if (flag)
            {
                result = 0;
            }
            else
            {
                int num2 = values[0];
                for (int i = 1; i < num; i++)
                {
                    bool flag2 = values[i] > num2;
                    if (flag2)
                    {
                        num2 = values[i];
                    }
                }
                result = num2;
            }
            return result;
        }

        /// <summary>
        ///   <para>Returns f raised to power p.</para>
        /// </summary>
        /// <param name="f"></param>
        /// <param name="p"></param>
        public static float Pow(float f, float p)
        {
            return (float)Math.Pow((double)f, (double)p);
        }

        /// <summary>
        ///   <para>Returns e raised to the specified power.</para>
        /// </summary>
        /// <param name="power"></param>
        public static float Exp(float power)
        {
            return (float)Math.Exp((double)power);
        }

        /// <summary>
        ///   <para>Returns the logarithm of a specified number in a specified base.</para>
        /// </summary>
        /// <param name="f"></param>
        /// <param name="p"></param>
        public static float Log(float f, float p)
        {
            return (float)Math.Log((double)f, (double)p);
        }

        /// <summary>
        ///   <para>Returns the natural (base e) logarithm of a specified number.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Log(float f)
        {
            return (float)Math.Log((double)f);
        }

        /// <summary>
        ///   <para>Returns the base 10 logarithm of a specified number.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Log10(float f)
        {
            return (float)Math.Log10((double)f);
        }

        /// <summary>
        ///   <para>Returns the smallest integer greater to or equal to f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Ceil(float f)
        {
            return (float)Math.Ceiling((double)f);
        }

        /// <summary>
        ///   <para>Returns the largest integer smaller than or equal to f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Floor(float f)
        {
            return (float)Math.Floor((double)f);
        }

        /// <summary>
        ///   <para>Returns f rounded to the nearest integer.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Round(float f)
        {
            return (float)Math.Round((double)f);
        }

        /// <summary>
        ///   <para>Returns the largest integer smaller to or equal to f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static int FloorToInt(float f)
        {
            return (int)Math.Floor((double)f);
        }

        /// <summary>
        ///   <para>Returns f rounded to the nearest integer.</para>
        /// </summary>
        /// <param name="f"></param>
        public static int RoundToInt(float f)
        {
            return (int)Math.Round((double)f);
        }

        /// <summary>
        ///   <para>Returns the sign of f.</para>
        /// </summary>
        /// <param name="f"></param>
        public static float Sign(float f)
        {
            return (f >= 0f) ? 1f : -1f;
        }

        /// <summary>
        ///   <para>Clamps the given value between the given minimum float and maximum float values.  Returns the given value if it is within the minimum and maximum range.</para>
        /// </summary>
        /// <param name="value">The floating point value to restrict inside the range defined by the minimum and maximum values.</param>
        /// <param name="min">The minimum floating point value to compare against.</param>
        /// <param name="max">The maximum floating point value to compare against.</param>
        /// <returns>
        ///   <para>The float result between the minimum and maximum values.</para>
        /// </returns>
        public static float Clamp(float value, float min, float max)
        {
            bool flag = value < min;
            if (flag)
            {
                value = min;
            }
            else
            {
                bool flag2 = value > max;
                if (flag2)
                {
                    value = max;
                }
            }
            return value;
        }

        /// <summary>
        ///   <para>Clamps the given value between a range defined by the given minimum integer and maximum integer values. Returns the given value if it is within min and max.</para>
        /// </summary>
        /// <param name="value">The integer point value to restrict inside the min-to-max range.</param>
        /// <param name="min">The minimum integer point value to compare against.</param>
        /// <param name="max">The maximum  integer point value to compare against.</param>
        /// <returns>
        ///   <para>The int result between min and max values.</para>
        /// </returns>
        public static int Clamp(int value, int min, int max)
        {
            bool flag = value < min;
            if (flag)
            {
                value = min;
            }
            else
            {
                bool flag2 = value > max;
                if (flag2)
                {
                    value = max;
                }
            }
            return value;
        }

        /// <summary>
        ///   <para>Clamps value between 0 and 1 and returns value.</para>
        /// </summary>
        /// <param name="value"></param>
        public static float Clamp01(float value)
        {
            bool flag = value < 0f;
            float result;
            if (flag)
            {
                result = 0f;
            }
            else
            {
                bool flag2 = value > 1f;
                if (flag2)
                {
                    result = 1f;
                }
                else
                {
                    result = value;
                }
            }
            return result;
        }

        /// <summary>
        ///   <para>Linearly interpolates between a and b by t.</para>
        /// </summary>
        /// <param name="a">The start value.</param>
        /// <param name="b">The end value.</param>
        /// <param name="t">The interpolation value between the two floats.</param>
        /// <returns>
        ///   <para>The interpolated float result between the two float values.</para>
        /// </returns>
        public static float Lerp(float a, float b, float t)
        {
            return a + (b - a) * Mathf.Clamp01(t);
        }

        /// <summary>
        ///   <para>Linearly interpolates between a and b by t with no limit to t.</para>
        /// </summary>
        /// <param name="a">The start value.</param>
        /// <param name="b">The end value.</param>
        /// <param name="t">The interpolation between the two floats.</param>
        /// <returns>
        ///   <para>The float value as a result from the linear interpolation.</para>
        /// </returns>
        public static float LerpUnclamped(float a, float b, float t)
        {
            return a + (b - a) * t;
        }

        /// <summary>
        ///   <para>Same as Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="t"></param>
        public static float LerpAngle(float a, float b, float t)
        {
            float num = Mathf.Repeat(b - a, 360f);
            bool flag = num > 180f;
            if (flag)
            {
                num -= 360f;
            }
            return a + num * Mathf.Clamp01(t);
        }

        /// <summary>
        ///   <para>Moves a value current towards target.</para>
        /// </summary>
        /// <param name="current">The current value.</param>
        /// <param name="target">The value to move towards.</param>
        /// <param name="maxDelta">The maximum change that should be applied to the value.</param>
        public static float MoveTowards(float current, float target, float maxDelta)
        {
            bool flag = Mathf.Abs(target - current) <= maxDelta;
            float result;
            if (flag)
            {
                result = target;
            }
            else
            {
                result = current + Mathf.Sign(target - current) * maxDelta;
            }
            return result;
        }

        /// <summary>
        ///   <para>Same as MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.</para>
        /// </summary>
        /// <param name="current"></param>
        /// <param name="target"></param>
        /// <param name="maxDelta"></param>
        public static float MoveTowardsAngle(float current, float target, float maxDelta)
        {
            float num = Mathf.DeltaAngle(current, target);
            bool flag = -maxDelta < num && num < maxDelta;
            float result;
            if (flag)
            {
                result = target;
            }
            else
            {
                target = current + num;
                result = Mathf.MoveTowards(current, target, maxDelta);
            }
            return result;
        }

        /// <summary>
        ///   <para>Interpolates between min and max with smoothing at the limits.</para>
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="t"></param>
        public static float SmoothStep(float from, float to, float t)
        {
            t = Mathf.Clamp01(t);
            t = -2f * t * t * t + 3f * t * t;
            return to * t + from * (1f - t);
        }

        public static float Gamma(float value, float absmax, float gamma)
        {
            bool flag = value < 0f;
            float num = Mathf.Abs(value);
            bool flag2 = num > absmax;
            float result;
            if (flag2)
            {
                result = (flag ? (-num) : num);
            }
            else
            {
                float num2 = Mathf.Pow(num / absmax, gamma) * absmax;
                result = (flag ? (-num2) : num2);
            }
            return result;
        }

        /// <summary>
        ///   <para>Compares two floating point values and returns true if they are similar.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        public static bool Approximately(float a, float b)
        {
            return Mathf.Abs(b - a) < Mathf.Max(1E-06f * Mathf.Max(Mathf.Abs(a), Mathf.Abs(b)), Mathf.Epsilon * 8f);
        }

        

        /// <summary>
        ///   <para>Loops the value t, so that it is never larger than length and never smaller than 0.</para>
        /// </summary>
        /// <param name="t"></param>
        /// <param name="length"></param>
        public static float Repeat(float t, float length)
        {
            return Mathf.Clamp(t - Mathf.Floor(t / length) * length, 0f, length);
        }

        /// <summary>
        ///   <para>PingPong returns a value that will increment and decrement between the value 0 and length.</para>
        /// </summary>
        /// <param name="t"></param>
        /// <param name="length"></param>
        public static float PingPong(float t, float length)
        {
            t = Mathf.Repeat(t, length * 2f);
            return length - Mathf.Abs(t - length);
        }

        /// <summary>
        ///   <para>Calculates the linear parameter t that produces the interpolant value within the range [a, b].</para>
        /// </summary>
        /// <param name="a">Start value.</param>
        /// <param name="b">End value.</param>
        /// <param name="value">Value between start and end.</param>
        /// <returns>
        ///   <para>Percentage of value between start and end.</para>
        /// </returns>
        public static float InverseLerp(float a, float b, float value)
        {
            bool flag = a != b;
            float result;
            if (flag)
            {
                result = Mathf.Clamp01((value - a) / (b - a));
            }
            else
            {
                result = 0f;
            }
            return result;
        }

        /// <summary>
        ///   <para>Calculates the shortest difference between two given angles given in degrees.</para>
        /// </summary>
        /// <param name="current"></param>
        /// <param name="target"></param>
        public static float DeltaAngle(float current, float target)
        {
            float num = Mathf.Repeat(target - current, 360f);
            bool flag = num > 180f;
            if (flag)
            {
                num -= 360f;
            }
            return num;
        }


        internal static long RandomToLong(Random r)
        {
            byte[] array = new byte[8];
            r.NextBytes(array);
            return (long)(BitConverter.ToUInt64(array, 0) & 9223372036854775807UL);
        }
        /// <summary>
        ///   <para>The well-known 3.14159265358979... value (Read Only).</para>
        /// </summary>
        public const float PI = 3.14159274f;

        /// <summary>
        ///   <para>A representation of positive infinity (Read Only).</para>
        /// </summary>
        public const float Infinity = float.PositiveInfinity;

        /// <summary>
        ///   <para>A representation of negative infinity (Read Only).</para>
        /// </summary>
        public const float NegativeInfinity = float.NegativeInfinity;

        /// <summary>
        ///   <para>Degrees-to-radians conversion constant (Read Only).</para>
        /// </summary>
        public const float Deg2Rad = 0.0174532924f;

        /// <summary>
        ///   <para>Radians-to-degrees conversion constant (Read Only).</para>
        /// </summary>
        public const float Rad2Deg = 57.29578f;

        /// <summary>
        ///   <para>A tiny floating point value (Read Only).</para>
        /// </summary>
        public static readonly float Epsilon = float.Epsilon;
    }
}


